import importlib
import logging
import logging.config
import os
import pkgutil
from contextlib import asynccontextmanager
from pathlib import Path

from fastapi import FastAPI, Depends
from fastapi_cache import FastAPICache
from fastapi_pagination import add_pagination
from starlette.staticfiles import StaticFiles

from middlewares import init_https_middleware
from admin.service.base_service import auto_register_services
from dependencies.cache import custom_key_builder, redis_be
from dependencies.database import db
from env_config import settings
from utils.console_logger import console_logger


def create_log_dir():
    logging_config = settings.LOGGING_DIC
    log_file_path = logging_config['handlers']['file']['filename']
    log_dir = os.path.dirname(log_file_path)
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)


create_log_dir()

logging.config.dictConfig(settings.LOGGING_DIC)
console_logger()

logger = logging.getLogger(__name__)


@asynccontextmanager
async def lifespan(app: FastAPI):
    """第一个lifespan，打开连接和初始化缓存，非第一个lifespan参考add_pagination"""
    # Load the ML model
    await db.connect()
    FastAPICache.init(redis_be, prefix=settings.redis_prefix, key_builder=custom_key_builder)
    yield
    await db.disconnect()


def configure_static(app: FastAPI):
    """配置静态资源"""
    # 上传路径创建
    if not os.path.exists(settings.upload_directory):
        os.makedirs(settings.upload_directory)
    # 上传路径配置
    app.mount(settings.upload_prefix, StaticFiles(directory=settings.upload_directory), name='upload')
    # 静态资源路径配置
    if settings.enabled_static:
        app.mount(settings.static_path, StaticFiles(directory=settings.static_directory), name='static')


def configure_middleware(app: FastAPI):
    """配置中间件"""
    from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
    from middlewares import init_cors_middleware, init_timeout_middleware

    app.add_middleware(ProxyHeadersMiddleware)
    init_cors_middleware(app)
    init_timeout_middleware(app)
    init_https_middleware(app)


def scan_routes(app: FastAPI, admin_deps, prefix='/api', package='admin.routers'):
    """
    扫描路由
    """
    # 获取当前包名
    current_package = __package__
    packages = package.split('.')
    # 获取当前目录下所有子模块
    package_path = Path(__file__).resolve().parent / '/'.join(packages)
    admin_package = f'{package}'
    if current_package:
        admin_package = f'{current_package}.{admin_package}'
    for _, name, _ in pkgutil.iter_modules([str(package_path)]):
        module_name = f'{admin_package}.{name}'
        module = importlib.import_module(module_name, package=admin_package)
        if hasattr(module, 'router'):
            # 提取路由器和元数据
            router = module.router
            if router.tags:
                app.include_router(router, prefix=prefix, dependencies=admin_deps)
            else:
                app.include_router(router, prefix=prefix, dependencies=admin_deps, tags=[f'{packages[0]}:{name}'])


def configure_router(app: FastAPI, prefix='/api'):
    """配置后台路由"""
    from dependencies.verify import verify_token

    # 后台依赖
    admin_deps = [Depends(verify_token)]

    # admin
    scan_routes(app, admin_deps, prefix=prefix)

    # 加载 generator 相关路由
    scan_routes(app, admin_deps, prefix=prefix, package='generator.routers')

    # 自动导入和注册 services 包中的所有服务类
    auto_register_services(app, 'admin.service')


def create_app() -> FastAPI:
    """创建FastAPI后台应用,并初始化"""
    from exceptions.global_exc import configure_exception

    docs_url = redoc_url = openapi_url = None
    if settings.mode == "dev":
        # 开发环境才启用文档
        docs_url = "/docs"
        redoc_url = "/redoc"
        openapi_url = "/openapi.json"

    app = FastAPI(lifespan=lifespan, title="语音会议纪要助手",
                  description="""
    🧠 语音会议纪要助手  
    提供音频转录、摘要生成、行动项提取等功能。
    """, version=settings.version, docs_url=docs_url, redoc_url=redoc_url,
                  openapi_url=openapi_url, )
    configure_static(app)
    configure_exception(app)
    configure_middleware(app)
    configure_router(app)
    add_pagination(app)

    return app
